import axios from "axios";
import qs from "qs";
import emitter from "../modules/emitter.js";

export function serializeParams(params) {
  return qs.stringify(params, {
    arrayFormat: 'repeat'
  });
}

function delay(ms) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(ms);
    }, ms);
  });
}

const instance = axios.create({
  baseURL: '/api',
  timeout: 15000,
  paramsSerializer: {
    serialize: serializeParams
  },
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded'
  }
});

instance.interceptors.request.use(config => {
  return config;
});

instance.interceptors.response.use(response => {
  return response.data;
}, error => {
  const response = error.response;
  if (response == null) {
    return Promise.reject({
      message: '网络连接失败 ~',
      response
    });
  }
  if (response.status === 404) {
    return Promise.reject({
      status: response.status,
      message: '请求资源不存在 ~',
      response
    });
  }
  if (response.status === 401) {
    emitter.emit('api.error', response);
    return Promise.reject({
      status: response.status,
      message: '登录失效，请重新登录 ~',
      response
    });
  }
  if (response.status === 500) {
    return Promise.reject({
      status: response.status,
      message: '系统繁忙，请稍后再试 ~',
      response
    });
  }
  if (response.data.message != null) {
    return Promise.reject({
      status: response.status,
      message: response.data.message,
      response
    });
  }
  return Promise.reject({
    status: response.status,
    message: '发生未知错误 ~',
    response
  });
});

export function apiGet(url, params, config) {
  return instance.get(url, {
    ...config,
    params
  });
}

export function apiPost(url, data, config) {
  const json = config.headers['Content-Type'] === 'application/json' || config.headers['Content-Type'] === 'multipart/form-data';

  return instance.post(url, json ? data : serializeParams(data), config);
}

export function apiPut(url, data, config) {
  return instance.put(url, serializeParams(data), config);
}

export function apiDelete(url, data, config) {
  return instance.delete(url, serializeParams(data), config);
}

/**
 * HTTP 请求
 * @param options {{ method?: 'GET' | 'POST' | 'PUT' | 'DELETE', immediate?: boolean, url: string, default?: [], data?: *, delay?: Number, showError?: boolean }}
 */
export function useApi(options) {
  options = {
    method: 'GET',
    immediate: true,
    showError: true,
    ...options
  };

  const isLoading = ref(false);
  const isFinished = ref(false);
  const isAborted = ref(false);
  const data = ref(options.default);
  const error = ref(null);
  const loading = loading => {
    isLoading.value = loading;
    isFinished.value = !loading;
  }

  const controller = new AbortController();
  const abort = reason => {
    if (!isLoading.value) {
      return;
    }

    controller.abort(reason);

    isAborted.value = true;
    loading(false);
  }


  /**
   * HTTP 请求
   * @param [executeOptions] {{ method: 'GET' | 'POST' | 'PUT' | 'DELETE', immediate: boolean, url: string, data: *, dataType: 'json' | 'form' }}
   * @returns { Promise }
   */
  const execute = async executeOptions => {
    executeOptions = {
      ...options,
      ...executeOptions
    };
    loading(true);


    if (options.delay != null) {
      await delay(options.delay);
    }

    const headers = {};
    if (executeOptions.dataType === 'json') {
      headers['Content-Type'] = 'application/json';
    } else if (executeOptions.dataType === 'file') {
      headers['Content-Type'] = 'multipart/form-data';
    }


    try {
      const apiMethod = (executeOptions.method === 'POST' ? apiPost
        : executeOptions.method === 'PUT' ? apiPut
          : executeOptions.method === 'DELETE' ? apiDelete
            : apiGet);

      // 动态url
      typeof executeOptions.url === "function" && (executeOptions.url = executeOptions.url());

      const responseData = await apiMethod(
        executeOptions.url,
        executeOptions.data,
        {
          signal: controller.signal,
          headers
        }
      );

      data.value = responseData;
      return responseData;
    } catch (e) {
      if (executeOptions.showError) {
        message.error(e.message);
      }
      error.value = e;
      throw e;
    } finally {
      loading(false);
    }
  }

  if (options.immediate) {
    execute(options);
  }

  return {
    isLoading,
    isFinished,
    isAborted,
    data,
    error,
    execute,
    abort
  };
}
